#pragma once

#include "LogMessage.hpp"

#include <iostream>
#include <cstring>
#include <string>
#include <memory>

#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>

#include <unistd.h>

class Sock {
public:
      // 1. 创建套接字
  int Socket() {
    int listenSock = socket(AF_INET, SOCK_STREAM, 0);

    if(listenSock == -1) {
      LogMessage(FATAL, _F, _L, "server create socket fail");
      exit(2);
    }
    LogMessage(DEBUG, _F, _L, "server create socket success, listen sock::%d", listenSock);
    return listenSock;
  }
      // 2.bind ip and port
  void Bind(int listenSock, uint16_t port, const std::string& ip = "0.0.0.0") {
    sockaddr_in local;
    memset(&local, 0, sizeof(local));

    local.sin_family = AF_INET;
    local.sin_port = htons(port);
    local.sin_addr.s_addr = inet_addr(ip.c_str());

    if(bind(listenSock, reinterpret_cast<sockaddr*>(&local), sizeof(local)) < 0) {
      LogMessage(FATAL, _F, _L, "server bind IP+port fail :: %d:%s", errno, strerror(errno));
      exit(3);
    }

    LogMessage(DEBUG, _F, _L, "server bind IP+port success");
  }

      // 3. listen Sock
  void Listen(int listenSock) {
    if(listen(listenSock, gBackLog) < 0) {
      LogMessage(FATAL, _F, _L, "server listen fail");
      exit(4);
    }
    LogMessage(NORMAL, _F, _L, "server init success");
  }
      
      // 4.accept接收连接      输出型参数，返回客户端的IP + port
  int Accept(int listenSock, std::string &clientIp, uint16_t &clientPort) {
    sockaddr_in clientMessage;
    socklen_t clientLen = sizeof(clientMessage);
    memset(&clientMessage, 0, clientLen);
    int serverSock = accept(listenSock, reinterpret_cast<sockaddr*>(&clientMessage), &clientLen);

    clientIp = inet_ntoa(clientMessage.sin_addr);
    clientPort = ntohs(clientMessage.sin_port);

    if(serverSock < 0) {
      LogMessage(ERROR, _F, _L, "server accept connection fail");
      return -1;
    } else {
      LogMessage(NORMAL, _F, _L, "server accept connection success ::[%s:%d] server sock::%d", clientIp.c_str(), clientPort, serverSock);
    }
    return serverSock;
  }
private:
    const int gBackLog = 20;
};

